{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Add my QComponent to a reusable python file\n",
    "\n",
    "### Preparations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Preload python packages."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%load_ext autoreload\n",
    "%autoreload 2\n",
    "import qiskit_metal as metal\n",
    "from qiskit_metal import designs, draw\n",
    "from qiskit_metal import MetalGUI, Dict"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create a design and open the GUI"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "design = designs.DesignPlanar()\n",
    "design.overwrite_enabled = True\n",
    "gui = MetalGUI(design)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Managing a QComponent\n",
    "#### Create an instance\n",
    "Create an instance of `TransmonPocket`. Notice the import needs to use the absolute path. This is to minimize the dynamic memory footprint of Qiskit Metal and to prevent QComponent sub-class naming conflicts that might occur if user creates custom ones.\n",
    "\n",
    "Observe how, at time of creation, we define some basic options of the TransmonPocket, being the number/name of connection pads(`a`->`d`). Most options can be defined/updated also after the creation of the QComponent instance, as we will show later in this section."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit_metal.qlibrary.qubits.transmon_pocket import TransmonPocket\n",
    "\n",
    "q1options = dict(\n",
    "    connection_pads=dict(  # pin connectors\n",
    "        a = dict(loc_W=+1,loc_H=+1), \n",
    "        b = dict(loc_W=-1,loc_H=+1),\n",
    "        c = dict(loc_W=+1,loc_H=-1),\n",
    "        d = dict(loc_W=-1,loc_H=-1)\n",
    "    )\n",
    ")\n",
    "\n",
    "q1 = TransmonPocket(design, options = q1options)  # this line only creates the object in memory and executes its __init__(), but does not \"implement\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Turn your attention to the Qiskit Metal GUI. In the `Main View` tab you will not see `q1` yet. However if you open the tab `Elements`, you will observe that all of its shapes (`component_id` = 1) are now present in the QGeometry tables (show in that tab). This occurs because the `__init__()` of the QComponent class already run the `make()` of the TransmonPocket.\n",
    "\n",
    "Note that the `make()` if the component does not automatically render to the GUI, for increased usage flexibility. Therefore we need to explicit call the method `gui.rebuild()` to observe the shapes in the `Main View`. We do that in the next cell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "gui.rebuild()  # this updates the QComponent tables by running make()\n",
    "gui.autoscale()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Controlling the instance using its interface parameters\n",
    "\n",
    "Now we can also change the options of the component `q1` before we re-run the make.\n",
    "\n",
    "QComponent classes can have parameterized `make()`. That means that the final shapes will be the result of a combination of input parameters. Said parameters and their defaults are described in the QComponent `default_options`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q1.default_options"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This list of options is also \"extended\" by the supported renderers. This is because the simulation/analysis can have shape- (or even QComponent-)dependent parameters to e set. See below the full list of options available for the user to update for the `TransmonPocket`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q1.options"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "These options can be modified at creation, using the `options` parameter of the QComponent init method. See `q1options` in the above cells. Later i this notebook, we will show how to update options at a later time (After creation).\n",
    "\n",
    "#### QComponent name changes\n",
    "The previously created TransmonPocket instance, was named `Pocket_1` by default (see in GUI or by executing `q1.name`). The first part of the name is defined in the QComponent class `component_metadata -> short name` as we will see later, while the suffix is an incremental integer resulting from a QComponent class-specific counter.\n",
    "\n",
    "Instead of using the default name, we can assign the QComponent instance name at creation, for example: `q1 = TransmonPocket(design, name = \"Q1\", options = q1options)`. We can also rename it like this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "design.rename_component(q1.id,'Q1')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Connecting two QComponents - Nets\n",
    "Let's demonstrate how to update the options of a component after creation by updating `pos_x` to move `Q1` towards the left. let's go ahead and create also an identical TransmonPocket (`Q2`) to its right."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q1.options.pos_x='-1.5mm'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "q2 = design.copy_qcomponent(q1, 'Q2')\n",
    "q2.options.pos_x='1.5mm'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To connect `Q1:a` to `Q2:b`, we will use a QComponent that inherits from the `QRoute` class. In this example, we use `RouteMeander`.\n",
    "\n",
    "Note that it is mandatory to provide the option `pin_inputs` for a QRoute to be created correctly. Indeed a QRoute generates its own pins by mirroring the pins of the QComponents it needs to connect.\n",
    "For `RouteMeander`, we also need to provide a `total_length`, since the meander shapes will be created to match the target transmission line length."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit_metal.qlibrary.tlines.meandered import RouteMeander\n",
    "\n",
    "options = Dict(\n",
    "    total_length = '8mm',\n",
    "    pin_inputs = Dict(\n",
    "        start_pin = Dict(\n",
    "            component = 'Q1',\n",
    "            pin = 'a'),\n",
    "        end_pin = Dict(\n",
    "            component = 'Q2',\n",
    "            pin = 'b')),\n",
    ")\n",
    "\n",
    "cpw = RouteMeander(design, options=options)\n",
    "gui.rebuild()\n",
    "gui.autoscale()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The meander was created without corner rounding because the fillet radius was not specified. Let's go ahead and update the RouteMeander instance to smooth out those edges."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cpw.options.fillet = '90um'\n",
    "cpw.options.lead.start_straight = '90um'\n",
    "\n",
    "gui.rebuild()\n",
    "gui.autoscale()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, observe how the `net_info` table has been updated to record the physical/electrical connection between the two TransmonPocket instances and the RouteMeander instance. Two nets were created connecting the 4 involved pins in pairs (2 pins of the RouteMenader instance and two pins one each Transmon Pocket instance).\n",
    "\n",
    "Nets always pair pins. Net can never connect more than 2 pins because every net represents a point contact between two QComponents. This could appear like an excessive constrain when compared to net definition in traditional circuit design. However, it helps normalize the quantum circuit design flow."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "design.net_info"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating a custom component from scratch\n",
    "#### The QComponent blueprint\n",
    "```\n",
    "class SomeComponent(QComponent):\n",
    "    default_options = Dict(…)\n",
    "    def make(self):\n",
    "        ...\n",
    "        self.add_qgeometry(…)\n",
    "        self.add_pin(…)\n",
    "```\n",
    "`SomeComponent` = The name of the new QComponent class, which should alway inherit from `QComponent`.<br>\n",
    "`default_options` = The interface for parameters that influence the shape generation of the `make()`.<br>\n",
    "`make()` = Math to generate the shapes implementing the QComponent instance.<br>\n",
    "`add_qgeometry()` = Method to create `path`, `poly` and `junction` in the QGeometry table.<br>\n",
    "`add_pin()` = Method to create the pins to connect the QComponent through QRoute components.\n",
    "\n",
    "#### Building from that blueprint\n",
    "First, let's free up space in the layout we just compoese in the previous section. Specifically let's eliminate the cpw and Q2 instances (We will reuse Q1).\n",
    "\n",
    "Note in the following cell I am intentionally using two different methods to delete a component, just to illustrate them. YOu can use only one of the two methods in your notebooks."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "design.delete_component('Q2')\n",
    "cpw.delete()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice that the `net_info` table is now empty because we eliminated `cpw`, which was connecting the QComponent instances."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "design.net_info"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For the remainder of this section, you will need the file `my429_qcomponent.py`, which contains incremantally complex examples of custom components, intended only for illustrative purposes.\n",
    "\n",
    "Your file `my429_qcomponents.py` could be a at a different location, so you might need to update appropriately the import in the next cell."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# from qiskit_metal.qlibrary.user_components.my429_qcomponents import MyQComponent1\n",
    "import sys\n",
    "sys.path.append('../../resources')\n",
    "from my429_qcomponents import MyQComponent1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The first class `MyQComponent1` has been designed to be as simple as possible. In fact, it is not even parameterized. It is a simple fixed-size rectangle with a pin on the left edge."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myQC = MyQComponent1(design, 'myQC')\n",
    "\n",
    "gui.rebuild()  # this is need to actually make() the component\n",
    "gui.autoscale()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Open the `my429_qcomponents.py` file to observe the class code. You will notice it only calls the two key methods `add_qgeometry()` and `add_pins()`. It is that simple to create a custom QComponent, you just need 2 lines of code. With the following cell you can observe that the two lines are already sufficient to route to the QComponent and get its geometries inside the QGeometry table."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "options = Dict(\n",
    "    total_length = '4mm',\n",
    "    pin_inputs = Dict(\n",
    "        start_pin = Dict(\n",
    "            component = 'Q1',\n",
    "            pin = 'a'),\n",
    "        end_pin = Dict(\n",
    "            component = myQC.name,\n",
    "            pin = 'in')),\n",
    "    fillet = '90um',\n",
    "    lead = Dict(\n",
    "        start_straight = '90um')\n",
    ")\n",
    "\n",
    "cpw = RouteMeander(design, options=options)\n",
    "gui.rebuild()\n",
    "gui.autoscale()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The previous class example did not allow for parametrization. Let's introduce that factor with the next example class `MyQComponent2`. First observe the different shapes by running the following cell, and then compare the code by opening again the `my429_qcomponents.py` file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cpw.delete()\n",
    "myQC.delete()\n",
    "\n",
    "# from qiskit_metal.qlibrary.user_components.my429_qcomponents import MyQComponent2\n",
    "from my429_qcomponents import MyQComponent2\n",
    "\n",
    "opt_myqc = Dict(width='1mm', height='0.01mm', pos_x='0.1mm')\n",
    "myQC = MyQComponent2(design, 'myQC', options=opt_myqc)\n",
    "\n",
    "cpw = RouteMeander(design, options=options)\n",
    "gui.rebuild()  # this is need to actually make() the component\n",
    "gui.autoscale()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Observe the available options with the next cell. These are only the options we listed in the `default_options`, the renderers did not add any analysis-related option to the list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myQC.options"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The reason the list of `options` and `default_options` match is because the class was not designed to import the renderer-dependent options from the renderer. To do that you will need to add the `component_metadata` to your class. We do that in `MyQComponent3` and `MyQComponent4`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "cpw.delete()\n",
    "myQC.delete()\n",
    "\n",
    "opt_myqc.gap = '8um'\n",
    "opt_myqc.height = '20um'\n",
    "# from qiskit_metal.qlibrary.user_components.my429_qcomponents import MyQComponent4\n",
    "from my429_qcomponents import MyQComponent4\n",
    "\n",
    "myQC = MyQComponent4(design, options=opt_myqc) #opt_myqc defined earlier\n",
    "\n",
    "options = Dict(\n",
    "    total_length = '4mm',\n",
    "    pin_inputs = Dict(\n",
    "        start_pin = Dict(\n",
    "            component = 'Q1',\n",
    "            pin = 'a'),\n",
    "        end_pin = Dict(\n",
    "            component = myQC.name, #updating this\n",
    "            pin = 'in')),\n",
    "    fillet = '90um',\n",
    "    lead = Dict(\n",
    "        start_straight = '90um')\n",
    ")\n",
    "\n",
    "cpw = RouteMeander(design, options=options)\n",
    "gui.rebuild()  # this is need to actually make() the component\n",
    "gui.autoscale()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Observe the additional renderer parameters being added to the list of options that the user can modify."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "myQC.options"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
